package com.chun.api;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import com.chun.api.ApiStore.ApiRunnable;
import com.fasterxml.jackson.databind.SerializationFeature;

public class ApiGateWayHand implements ApplicationContextAware,InitializingBean{
	private static final String METHOD = "method";
	private static final String PARAMS = "params";
	ApiStore apiStore;
	final ParameterNameDiscoverer parameterUtil;
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		apiStore = new ApiStore(context);
		
	}
	public ApiGateWayHand(){
		parameterUtil = new LocalVariableTableParameterNameDiscoverer();
	}

	public void afterPropertiesSet() throws Exception {
		apiStore.loadApiFromSpringBeans(); 
		
	}
	public void handle(HttpServletRequest req, HttpServletResponse resp) {
		String params = req.getParameter("params");
		String method = req.getParameter("method");
		Object result;
		ApiRunnable apiRun = null;
		try {
			apiRun = sysParamsValdate(req);
			Object[] args = buildParams(apiRun,params,req,resp);
			result = apiRun.run(args);
		} catch (ApiException e) {
			// TODO: handle exception
			resp.setStatus(500);
			result = handError(e);
		} catch (InvocationTargetException e) {
			// TODO: handle exception
			resp.setStatus(500);
			System.out.println("调用接口=["+method +"]异常   参数=[" + params +"]" );
			result = handError(e.getTargetException());
		} catch (Exception e) {
			// TODO: handle exception
			resp.setStatus(500);
			result = handError(e);
		}
		
		
		returnResult(result,resp);
	}


	private void returnResult(Object result, HttpServletResponse resp)  {
		 try {
			 UtilJson.JSON_MAPPER.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, true);
			 String json = UtilJson.writeValueAsString(result);
			 resp.setCharacterEncoding("UTF-8");
			 resp.setContentType("text/html/json;charset=utf-8");
			 resp.setHeader("Cache-Control", "no-cache");
			 resp.setHeader("Pragma", "no-cache");
			 resp.setDateHeader("Expires", 0);
			 if (json != null) {
				resp.getWriter().write(json);
			}
		} catch (IOException e) {
			 throw new RuntimeException(e);
		}
	}


	private Object handError(Throwable throwable) {
		String code = "";
		String message = throwable.getMessage();
		if (throwable instanceof ApiException) {
			code = "0001";
			message = throwable.getMessage();
		}else if (throwable instanceof Exception) {
			
		}else {
			code = "0002";
		} 
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("error", code);
		result.put("msg", message);
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		PrintStream psm = new PrintStream(bos);
		throwable.printStackTrace(psm);
		return result;
	}


	private Object[] buildParams(ApiRunnable apiRun, String paramsJson, HttpServletRequest req, HttpServletResponse resp) throws ApiException {
		Map<String, Object> map = null;
		try {
			map = UtilJson.toMap(paramsJson);
		} catch (IllegalArgumentException e) {
			throw new ApiException("调用失败：josn字符串格式异常，请检查参数params参数");
		}
		if (map == null ) {
			map = new HashMap<String, Object>();
		}
		Method method = apiRun.targetMethond;
		 
		List<String> paramNames = Arrays.asList(parameterUtil.getParameterNames(method));
		
		
		Class<?>[] paramTypes = method.getParameterTypes();
		for(Map.Entry<String, Object> m:map.entrySet()){
			if (!paramNames.contains(m.getKey())) {
				throw new ApiException("调用失败：接口不存在`" + m.getKey() 
				+ "`参数");
				
			}
		}
		Object[] args = new Object[paramTypes.length];
		for(int i = 0; i< paramTypes.length; i++){
			if (paramTypes[i].isAssignableFrom(HttpServletRequest.class)) {
				args[i] = req;
			}else if (map.containsKey(paramNames.get(i))) {
				try {
					args[i] = convertJsonToBean(map.get(paramNames.get(i)),paramTypes[i]);
				} catch (Exception e) {
					throw new ApiException("调用失败：指定参数格式错误或值错误`"  +paramNames.get(i) +"` , errorMsg`" + e.getMessage() +"`" );
				}
			}else{
				args[i] = null;
			}
		}
		return args;
	}


	private Object convertJsonToBean(Object val, Class<?> targetClas) {
		Object result = null;
		if(val == null){
			return null;
		} else if (Integer.class.equals(targetClas)) {
			Integer.parseInt(val.toString());
		}else if (Long.class.equals(targetClas)) {
			Long.parseLong(val.toString());
		}else if (Date.class.equals(targetClas)) {
			if (val.toString().matches("[0-9]+")) {
				result = new Date(Long.parseLong(val.toString()));
			}else {
				throw new IllegalArgumentException("日期必须是长整型时间戳");
			}
		}else if (String.class.equals(targetClas)) {
			if (val instanceof String) {
				result = val;
			}else {
				throw new IllegalArgumentException("转换目标类型为字符串");
				
			}
		}else {
			result = UtilJson.convertValue(val, targetClas);
		}
		return result;
	}


	private ApiRunnable sysParamsValdate(HttpServletRequest req) throws ApiException {
		String apiName = req.getParameter(METHOD);
		String json= req.getParameter(PARAMS);
		
		ApiRunnable api ;
		if (apiName == null  || apiName.trim().equals("")) {
			throw new ApiException("调用失败： 参数methon为空");
		}else if (json == null) {
			throw new ApiException("调用失败： 参数params为空");
		}else if ((api = apiStore.findApiRunnable(apiName))!=null) {
			throw new ApiException("调用是啊比： 指定API不存在，API：" + apiName);
		}
		
		// 多一个签名参数，作为app 跟 服务进行认证，这里的安卓调用不会进行 oauth2 来实现
		return api;
	}

 

}
